use serde::Deserialize;
use serde_json::{json, Value};
use std::time::Instant;

use crate::wallet::Client;
use crate::Error;
pub struct Xchs {
    client: Client,
    wallet_id: usize,
    fingerprint: String,
    tick: String,
    time: usize,
    fee: usize,
    wait_time: usize,
    target_address: String,
    delay: u64,
    amt: usize,
}

#[derive(Debug, Deserialize)]
struct Success {
    // 如果JSON中的字段名和Rust结构体的字段名不一致，可以使用 #[serde(rename = "json_field_name")] 进行映射
    success: bool,
    // 添加其他需要解析的字段
}

#[derive(Debug, Deserialize)]
struct Transaction {
    confirmed: bool,
    // 还可以添加其他需要的字段
}

#[derive(Debug, Deserialize)]
struct TransResp {
    transaction: Transaction,
}

struct TransItem {
    address: String,
    transaction_id: String,
    transaction_time: Instant,
}

struct MarkTransItem {
    trans_item: TransItem,
    is_out_time: bool,
    is_finish: bool,
}

impl Xchs {
    pub async fn new(
        ssl_file: &str,
        fingerprint: &str,
        wallet_id: usize,
        tick: &str,
        time: usize,
        fee: usize,
        wait_time: usize,
        target_address: &str,
        delay: u64,
        amt: usize,
    ) -> Result<Self, Error> {
        let ssl_crt = format!("{}\\wallet\\private_wallet.crt", ssl_file);
        let ssl_key = format!("{}\\wallet\\private_wallet.key", ssl_file);
        let client = Client::new("localhost", 9256, ssl_key, ssl_crt).await?;

        Ok(Xchs {
            client,
            wallet_id,
            fingerprint: fingerprint.to_string(),
            tick: tick.to_string(),
            time,
            fee,
            wait_time,
            target_address: target_address.to_string(),
            delay,
            amt,
        })
    }

    // fission裂变
    pub async fn mint(&mut self) -> Result<(), reqwest::Error> {
        if self.target_address.is_empty() {
            println!("执行裂变模式");
            loop {
                if self.fission_mint().await? {
                    break;
                }
            }
        } else {
            println!("执行正常模式");
            loop {
                if self.normal_mint().await? {
                    break;
                }
            }
        }

        Ok(())
    }

    pub async fn normal_mint(&mut self) -> Result<bool, reqwest::Error> {
        let mut mint_addresse_trans: Vec<TransItem> = Vec::new();
        let coins_nums = self.get_coins_num().await?;
        if coins_nums.len() < 25 {
            panic!("检测到当前钱包的有效spendble_coin过少，会影响铸造速度，建议先执行裂变模式，详细方法请阅读说明");
        }
        println!("本轮大币集合，他们将用于本轮的支出{:?}", coins_nums);

        // 设置本轮铸造的数量
        let mint_max_num = coins_nums.len();
        let mint_num = if mint_max_num > self.time {
            self.time
        } else {
            mint_max_num
        };
        println!(
            "当前剩余铸造数量为:{},遍历所有coins得出结论,本轮将铸造{}个",
            self.time, mint_num
        );
        let mut is_finish = false;
        for coin_num in coins_nums {
            let address = &self.target_address;
            std::thread::sleep(std::time::Duration::from_secs(self.delay));
            let transaction_id = self.send_transaction(&address, 1).await?;
            println!(
                "已成功发送了带有指定memo的交易:{{transaction_id:{}}},您可以到钱包界面进行查看进展",
                transaction_id
            );
            std::thread::sleep(std::time::Duration::from_secs(self.delay));

            mint_addresse_trans.push(TransItem {
                address: address.clone(),
                transaction_id,
                transaction_time: Instant::now(),
            });
            self.time = self.time - 1;
            if self.time <= 0 {
                is_finish = true;
                break;
            }
        }
        // 将mint_addresse_trans转化为：Vec<Some(TransItem)>
        let mut mint_addresse_trans: Vec<MarkTransItem> = mint_addresse_trans
            .into_iter()
            .map(|item| MarkTransItem {
                trans_item: item,
                is_out_time: false,
                is_finish: false,
            })
            .collect();

        // 循环检测是否完成交易，等待一定时间后，取消链上交易，并对is_finish进行更正，对self.time 进行修正
        loop {
            // 检查是否铸造完成
            for item in &mut mint_addresse_trans {
                if !item.is_out_time && !item.is_finish {
                    // 若没有超期，并且没有完成，就进行检查
                    // 先判断是否完成铸造
                    if self
                        .get_transaction(&item.trans_item.transaction_id)
                        .await?
                    {
                        item.is_finish = true;
                        println!(
                            "{{transaction_id:{},address:{}}}上链成功",
                            item.trans_item.transaction_id, item.trans_item.address
                        );
                        write_address(&item.trans_item.address).unwrap();
                    } else {
                        println!(
                            "{{transaction_id:{}}},等待交易完成,别慌,超时自动删除该笔交易,会自动进入下一轮。。。",
                            item.trans_item.transaction_id
                        );

                        // 若没有完成，就检查是否超期
                        if item.trans_item.transaction_time.elapsed().as_secs() as usize
                            > self.wait_time
                        {
                            item.is_out_time = true
                        }
                    }
                    std::thread::sleep(std::time::Duration::from_secs(self.delay));
                }
            }

            // 判断是否可以结束循环并标记超时个数
            let mut out_time_num = 0_usize;

            let mut num = 0_usize;

            for item in mint_addresse_trans.iter() {
                if item.is_finish || item.is_out_time {
                    num += 1
                }
                if item.is_out_time {
                    out_time_num += 1
                }
            }

            if num == mint_addresse_trans.len() {
                if out_time_num > 0 {
                    self.delete_unconfirmed_transactions().await?;
                    self.time += out_time_num;
                    is_finish = false;
                }
                break;
            }
        }

        Ok(is_finish)
    }

    // mint
    pub async fn fission_mint(&mut self) -> Result<bool, reqwest::Error> {
        let mut mint_addresse_trans: Vec<TransItem> = Vec::new();
        let coins_nums = self.get_coins_num().await?;
        println!("本轮大币集合，他们将用于本轮的支出{:?}", coins_nums);
        let mint_max_num = coins_nums.len();
        let mint_num = if mint_max_num > self.time {
            self.time
        } else {
            mint_max_num
        };
        println!(
            "当前剩余铸造数量为:{},遍历所有coins得出结论,本轮铸造将同时铸造{}个",
            self.time, mint_num
        );
        let mut is_finish = false;
        for coin_num in coins_nums {
            let address = self.get_new_address().await?;
            println!("在本地钱包新建了地址并开始铸造{}", address);
            std::thread::sleep(std::time::Duration::from_secs(self.delay));
            let send_amount = coin_num / 2;
            let transaction_id = if send_amount > self.fee {
                self.send_transaction(&address, send_amount).await?
            } else {
                self.send_transaction(&address, 1).await?
            };
            println!(
                "已成功发送了带有指定memo的交易:{{transaction_id:{}}},您可以到钱包界面进行查看进展",
                transaction_id
            );
            std::thread::sleep(std::time::Duration::from_secs(self.delay));

            mint_addresse_trans.push(TransItem {
                address,
                transaction_id,
                transaction_time: Instant::now(),
            });
            self.time = self.time - 1;
            if self.time <= 0 {
                is_finish = true;
                break;
            }
        }

        // 将mint_addresse_trans转化为：Vec<Some(TransItem)>
        let mut mint_addresse_trans: Vec<MarkTransItem> = mint_addresse_trans
            .into_iter()
            .map(|item| MarkTransItem {
                trans_item: item,
                is_out_time: false,
                is_finish: false,
            })
            .collect();

        // 循环检测是否完成交易，等待一定时间后，取消链上交易，并对is_finish进行更正，对self.time 进行修正
        loop {
            // 检查是否铸造完成
            for item in &mut mint_addresse_trans {
                if !item.is_out_time && !item.is_finish {
                    // 若没有超期，并且没有完成，就进行检查
                    // 先判断是否完成铸造
                    if self
                        .get_transaction(&item.trans_item.transaction_id)
                        .await?
                    {
                        item.is_finish = true;
                        println!(
                            "{{transaction_id:{},address:{}}}上链成功",
                            item.trans_item.transaction_id, item.trans_item.address
                        );
                        write_address(&item.trans_item.address).unwrap();
                    } else {
                        println!(
                            "{{transaction_id:{}}},等待交易完成,别慌,超时自动删除该笔交易,会自动进入下一轮。。。",
                            item.trans_item.transaction_id
                        );
                        // 若没有完成，就检查是否超期
                        if item.trans_item.transaction_time.elapsed().as_secs() as usize
                            > self.wait_time
                        {
                            item.is_out_time = true
                        }
                    }
                    std::thread::sleep(std::time::Duration::from_secs(self.delay));
                }
            }

            // 判断是否可以结束循环并标记超时个数
            let mut out_time_num = 0_usize;

            let mut num = 0_usize;

            for item in mint_addresse_trans.iter() {
                if item.is_finish || item.is_out_time {
                    num += 1
                }
                if item.is_out_time {
                    out_time_num += 1
                }
            }

            if num == mint_addresse_trans.len() {
                if out_time_num > 0 {
                    self.delete_unconfirmed_transactions().await?;
                    self.time += out_time_num;
                    is_finish = false;
                }
                break;
            }
        }

        Ok(is_finish)
    }

    // chia rpc wallet delete_unconfirmed_transactions '{"wallet_id": 2}'
    pub async fn delete_unconfirmed_transactions(&self) -> Result<(), reqwest::Error> {
        let body = json!({"wallet_id": self.wallet_id,"fingerprint":self.fingerprint}).to_string();
        loop {
            let resp = self
                .client
                .cmd("delete_unconfirmed_transactions", &body)
                .await?
                .json::<Value>()
                .await?;
            let is_success: Success = serde_json::from_value(resp.clone()).unwrap();
            if is_success.success {
                break;
            } else {
                println!("\r钱包未同步或操作过于频繁，正在进行智能等待。。。");
                std::thread::sleep(std::time::Duration::from_secs(self.delay));
            }
        }
        Ok(())
    }

    /* chia rpc wallet get_transaction
    '{"transaction_id": "0xab1090bc0624fff94610d698eb71ff10c49b091a668270fd00898118cb780cf1039a"}'*/

    pub async fn get_transaction(&self, transaction_id: &str) -> Result<bool, reqwest::Error> {
        let body =
            json!({"wallet_id": self.wallet_id,"transaction_id": transaction_id,"fingerprint":self.fingerprint}).to_string();
        let resp = loop {
            let resp = self
                .client
                .cmd("get_transaction", &body)
                .await?
                .json::<Value>()
                .await?;
            let is_success: Success = serde_json::from_value(resp.clone()).unwrap();
            if is_success.success {
                break resp;
            } else {
                println!("钱包未同步或操作过于频繁，正在进行智能等待。。。");
                std::thread::sleep(std::time::Duration::from_secs(self.delay));
            }
        };

        let result: TransResp = serde_json::from_value(resp).unwrap();

        // "success":false,

        Ok(result.transaction.confirmed)
    }

    pub async fn get_coins_num(&self) -> Result<Vec<usize>, reqwest::Error> {
        println!("正在索引本地所有可用的coin,这可能花费较长时间,请耐心等待。。。。");
        let spendable_coins = self.get_spendable_coins().await?;

        // 获取 confirmed_records 数组
        if let Some(confirmed_records) = spendable_coins["confirmed_records"].as_array() {
            // 获取 confirmed_records 数组的元素数量
            let mut count = confirmed_records.len();
            let mut coin_amounts = Vec::new();
            for coin in confirmed_records {
                let amount = coin["coin"]["amount"].to_string().parse::<usize>().unwrap();
                if amount <= self.fee {
                    println!(
                        "检测到了金额小于手续费的coin:{}",
                        coin["coin"]["puzzle_hash"].to_string()
                    );
                    println!("将本轮规划铸造数量降低1次");
                    count -= 1;
                } else {
                    println!(
                        "{{\"puzzle_hash\":{},\"amount\":{}}}",
                        coin["coin"]["puzzle_hash"].to_string(),
                        amount
                    );
                    coin_amounts.push(amount);
                }
            }
            println!("本轮计划铸造数量为:{}", count);
            // coin_amounts对其进行排序，从大到小排列
            coin_amounts.sort_by(|a, b| b.cmp(a));
            return Ok(coin_amounts);
        } else {
            panic!("confirmed_records is not an array");
        }
    }

    pub async fn get_new_address(&self) -> Result<String, reqwest::Error> {
        let body = json!({"fingerprint": self.fingerprint,"wallet_id": self.wallet_id, "new_address": true});
        let body = serde_json::to_string(&body).unwrap();
        let resp = loop {
            let resp = self
                .client
                .cmd("get_next_address", &body)
                .await?
                .json::<Value>()
                .await?;
            let is_success: Success = serde_json::from_value(resp.clone()).unwrap();
            if is_success.success {
                break resp;
            } else {
                println!("钱包未同步或操作过于频繁，正在进行智能等待。。。");
                std::thread::sleep(std::time::Duration::from_secs(self.delay));
            }
        };

        Ok(resp["address"].as_str().unwrap().to_string())
    }

    // chia rpc wallet get_spendable_coins '{"wallet_id": self.wallet_id,"fingerprint":self.fingerprint}'

    pub async fn get_spendable_coins(&self) -> Result<Value, reqwest::Error> {
        let min_coin_amount = self.fee + 2;
        let body = json!({"min_coin_amount":min_coin_amount,"wallet_id": self.wallet_id,"fingerprint":self.fingerprint}).to_string();
        let resp = loop {
            let resp = self
                .client
                .cmd("get_spendable_coins", &body)
                .await?
                .json::<Value>()
                .await?;
            let is_success: Success = serde_json::from_value(resp.clone()).unwrap();
            if is_success.success {
                println!("{}", resp.to_string());
                break resp;
            } else {
                println!("钱包未同步或操作过于频繁，正在进行智能等待。。。");
                std::thread::sleep(std::time::Duration::from_secs(self.delay));
            }
        };
        Ok(resp)
    }

    /* chia rpc wallet send_transaction
    '{"wallet_id": 1, "address": "xch1fev2qaclwpcue9kx4p39dzfxpzaavvcz10v3lhx77cxha7f0tjjlsngh10k0",
    "amount": 1000, "fee": 1, "memos":["memo1"]}'*/

    pub async fn send_transaction(
        &self,
        address: &str,
        amount: usize,
    ) -> Result<String, reqwest::Error> {
        let memos = format!(
            "{{'{}':'{}','{}':'{}','{}':'{}','{}':'{}'}}",
            "p", "xchs", "op", "mint", "tick", self.tick, "amt", self.amt
        );
        // let memos = json!([memos]);

        // min_coin_amount
        let min_coin_amount = amount + self.fee + 1;

        let body = json!({
            "wallet_id": self.wallet_id, 
            "address": address,
            "amount": amount, 
            "fee": self.fee, 
            "min_coin_amount":min_coin_amount,
            "memos":[memos]})
        .to_string();

        let resp = loop {
            let resp = self
                .client
                .cmd("send_transaction", &body)
                .await?
                .json::<Value>()
                .await?;
            let is_success: Success = serde_json::from_value(resp.clone()).unwrap();
            if is_success.success {
                break resp;
            } else {
                println!("钱包未同步或操作过于频繁，正在进行智能等待。。。");
                std::thread::sleep(std::time::Duration::from_secs(self.delay));
            }
        };

        Ok(resp["transaction_id"].as_str().unwrap().to_string())
    }

    // /send_transaction_multi
    pub async fn send_transaction_multi(&self) -> Result<String, reqwest::Error> {
        let body = json!(
                    {
          "wallet_id": 1,
          "additions": [
            {
              "amount": 1,
              "puzzle_hash": "0xfb983d30679c8b90c7cf1a676a14a84e3b41c0412ce167c57183f031b1cc40a5",
              "memos": ["hello"],
            },
            {
              "amount": 1,
              "puzzle_hash": "0x0635b599c09a05d3fc8e7e3f0c84364f6c391cb4eb947975963529a5cfd9bda4",
              "memos": ["world"],
            }
          ],
          "fee": 1,

        })
        .to_string();
        let resp = self
            .client
            .cmd("send_transaction_multi", &body)
            .await?
            .json::<Value>()
            .await?;

        Ok(resp.to_string())
    }
}

use std::fs::OpenOptions;
use std::io::{self, Write};
pub fn write_address(text_to_append: &str) -> io::Result<()> {
    // 文件路径
    let file_path = "address.txt";

    // 要追加到最后一行的文本

    // 打开文件
    let mut file = OpenOptions::new().append(true).open(file_path)?;
    let text_to_append = format!("\n{}", text_to_append);

    file.write_all(text_to_append.as_bytes())?;

    println!(
        "已经将address:({})记录在“.\\address.txt",
        text_to_append.trim()
    );
    Ok(())
}
